#include "Physics.h"

// Initialize the instance to null
Physics* Physics::m_PhysicsInstance = 0;

Physics::Physics(void)
{
}

Physics::~Physics(void)
{
}

Physics* Physics::GetInstance(void)
{
	// Create the instance if it doesn't exist
	if(!m_PhysicsInstance)
		m_PhysicsInstance = new Physics;

	// Return the singleton
	return m_PhysicsInstance;
}

void Physics::DeleteInstance(void)
{
	// Delete the physics instance
	if(m_PhysicsInstance)
	{
		delete m_PhysicsInstance;
		m_PhysicsInstance = NULL;
	}
}

void Physics::InitializePhysics(IDirect3DDevice9* m_pD3DDevice)
{
	// Get the device 
	m_Device = m_pD3DDevice;

	// Get an instance of the model manager
	m_pModelManager = ModelManager::GetInstance();

	// Set the max position a player can jump
	fMaxJump = 18.0f;
	// Set maximum velocity, we do not want the player to go faster than this
	fMaxVel = 10.0f;
	fMaxRot = 17.0f;
	fRotScalar = 0.03f;
	bRecentCollision = false;
}

void Physics::GravityCheck(int iAmountTracks, float deltaTime, bool bJumpOn)
{
	// Traverse through all the tracks to see if it is colliding with any of them
	for(int i = 1; i <= iAmountTracks; i++)
	{
		if(!m_pModelManager->CollisionDetection(i))
			bGravity = true;
		else
		{
			// Collision detection with track has happened
			// check to see if track piece is a corner piece
			if(m_pModelManager->GetBCornerPiece(i))
			{
				// If it is a corner piece check additional collision to see if player 
				// should still be falling
			if(m_pModelManager->CornerCollision(i))
				{
					if(GravityCheck(iAmountTracks, i))
					{
						bGravity = false;
						return;
					}
					else
					{
						bGravity = true;
						break;
					}
				}
				else
					return;
			}		
			else // If not a corner piece, then just return
				return;
		}
	}
	
	// If gravity is on, then keep moving the player down
	if(bGravity && !bJumpOn)
		m_pModelManager->ModifyModelPos(0, 0.0f, (-9.8f * deltaTime), 0.0f);
}

// Additional collision check to see if the open space of the corner piece is overlapped
// with a another track piece.
bool Physics::GravityCheck(int iAmountTracks, int iTrack)
{
	for(int i = 1; i <= iAmountTracks; i++)
	{
		if(i != iTrack)
		{
			if(m_pModelManager->CollisionDetection(i))
			{
				if(m_pModelManager->GetBCornerPiece(i))
				{
					if(m_pModelManager->CornerCollision(i))
					{
						// Overlapped empty space allow player to fall through
						return false; 
					}
					else
					{
						// No empty space so player will not fall through
						return true;
					}
				}
				// collided with another track piece do not fall through
				return true;
			}
		}
	}
	// no additional collision
	return false;
}

bool Physics::Jump(float fDeltaTime)
{
	// Send the player flying
	if(m_pModelManager->modelList[0]->vecPos.y < fMaxJump)
	{
		// Turn off gravity
		bGravity = false;
		m_pModelManager->ModifyModelPos(0, 0.0f, (9.8f * fDeltaTime), 0.0f);
		return true;
	}
	else
	{
		bGravity = true;
		return false;
	}
}

void Physics::FallingDown(float fDeltaTime)
{
	m_pModelManager->ModifyModelPos(0, 0.0f, (-9.8f * fDeltaTime), 0.0f);
}

void Physics::ResetVelocity(void)
{
	// After death or new level/reset velocity and rotation
	fCurVelX = 0.0f;
	fCurVelZ = 0.0f;
	fCurRotY = 0.0f;
	fCurRotZ = 0.0f;
}

float Physics::GetCurVelX(void)
{
	return fCurVelX;
}

float Physics::GetCurVelZ(void)
{
	return fCurVelZ;
}

void Physics::ChangeVelocityZPos(float fVelChange, float fDeltaTime)
{
	// Changing velocity to move in the positive Z direction
	fCurVelZ += (fVelChange * fDeltaTime);
	if (fCurVelZ > (fMaxVel * fDeltaTime))
		fCurVelZ = fMaxVel * fDeltaTime;
	fCurRotY += (fRotScalar * fDeltaTime);
	if (fCurRotY > fMaxRot)
		fCurRotY = fMaxRot;
	m_pModelManager->ModifyModelPos(0, 0.0f, 0.0f, fCurVelZ);	
	m_pModelManager->ModifyModelRot(0, 0.0f, fCurRotY, 0.0f);
}

void Physics::ChangeVelocityZNeg(float fVelChange, float fDeltaTime)
{
	// Velocity is Changing in the negative Z direction
	fCurVelZ -= ( fVelChange * fDeltaTime );
	if (fCurVelZ < ((fMaxVel * -1) * fDeltaTime))
		fCurVelZ = ((fMaxVel * -1) * fDeltaTime);
	fCurRotY -= (fRotScalar * fDeltaTime);
	if( fCurRotY < (fMaxRot * -1) )
	{
		fCurRotY = fMaxRot * -1;
	}
	m_pModelManager->ModifyModelPos(0, 0.0f, 0.0f, fCurVelZ);
	m_pModelManager->ModifyModelRot(0, 0.0f, fCurRotY, 0.0f);
}

void Physics::ChangeVelocityXPos(float fVelChange, float fDeltaTime)
{
	// Velocity is changing in the positive X Direction
	fCurVelX += (fVelChange * fDeltaTime);
	if (fCurVelX > (fMaxVel * fDeltaTime))
		fCurVelX = fMaxVel * fDeltaTime;
		fCurRotZ -= (fRotScalar * fDeltaTime);
	if( fCurRotZ < fMaxRot * -1 )
		fCurRotZ = (fMaxRot * -1);
	m_pModelManager->ModifyModelPos(0, fCurVelX, 0.0f, 0.0f);
	m_pModelManager->ModifyModelRot(0, 0.0f, 0.0f, fCurRotZ);
}

void Physics::ChangeVelocityXNeg(float fVelChange, float fDeltaTime)
{
	// Velocity is changin the negative X direction
	fCurVelX -= (fVelChange * fDeltaTime);
	if (fCurVelX < ((fMaxVel * -1) * fDeltaTime))
		fCurVelX = (fMaxVel * -1) * fDeltaTime;
	fCurRotZ += (fRotScalar * fDeltaTime);
	if( fCurRotZ > fMaxRot)
		fCurRotZ = (fMaxRot);
	m_pModelManager->ModifyModelPos(0, fCurVelX, 0.0f, 0.0f);
	m_pModelManager->ModifyModelRot(0, 0.0f, 0.0f, fCurRotZ);
}

void Physics::NegateVelocity(float fDeltaTime)
{
	// if we have not collided within the last half second
	// then negate current velocity
	if(!bRecentCollision)
	{		
		fCurVelZ *= -1;
		fCurRotY *= -1;
		bRecentCollision = true;
		lStartTime = (long)timeGetTime();

		//fCurVelX = fCurVelX * -1;
		m_pModelManager->ModifyModelPos(0, 0.0f, 0.0f, fCurVelZ);
		m_pModelManager->ModifyModelRot(0, 0.0f, fCurRotY, 0.0f);
	}
	else
	{
		lCurrentTime = (long)timeGetTime();
		lElapsedTime = lCurrentTime - lStartTime;
		if(lElapsedTime > 150)
		{
			bRecentCollision = false;
		}
	}
}

void Physics::SlowVelocityZ(float fVelChange, float fDeltaTime)
{
	// When not pushing W or S slow velocity to 0
	if( fCurVelZ > 0.0 )
	{
		fCurVelZ -= (fVelChange * fDeltaTime);
		m_pModelManager->ModifyModelPos(0, 0.0f, 0.0f, fCurVelZ);		
	}
	else if ( fCurVelZ < 0.0 )
	{
		fCurVelZ += (fVelChange * fDeltaTime);
		m_pModelManager->ModifyModelPos(0, 0.0f, 0.0f, fCurVelZ);
	}
	if( fCurRotY > 0.0 )
	{
		fCurRotY -= (fRotScalar * fDeltaTime);
		m_pModelManager->ModifyModelRot(0, 0.0f, fCurRotY, 0.0f);
	}
	else if( fCurRotY < 0.0 )
	{
		fCurRotY += (fRotScalar * fDeltaTime);
		m_pModelManager->ModifyModelRot(0, 0.0f, fCurRotY, 0.0f);
	}
}

void Physics::SlowVelocityX(float fVelChange, float fDeltaTime)
{
	// When not pushing A or D slow velocity down to 0
	if( fCurVelX > 0 )
	{
		fCurVelX -= (fVelChange * fDeltaTime);
		m_pModelManager->ModifyModelPos(0, fCurVelX, 0.0f, 0.0f);
	}
	else if ( fCurVelX < 0.0)
	{
		fCurVelX += (fVelChange * fDeltaTime);
		m_pModelManager->ModifyModelPos(0, fCurVelX, 0.0f, 0.0f);
	}
	if( fCurRotZ > 0 )
	{
		fCurRotZ -= (fRotScalar * fDeltaTime);
		m_pModelManager->ModifyModelRot(0, 0.0f, 0.0f, fCurRotZ);
	}
	else if( fCurRotZ < 0 )
	{
		fCurRotZ += (fRotScalar * fDeltaTime);
		m_pModelManager->ModifyModelRot(0, 0.0f, 0.0f, fCurRotZ);
	}
}